/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package com.lineage;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.lineage.configure.ConfigureSQL;
import com.lineage.server.utils.LeakCheckConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * Class <code>L1DatabaseFactory</code> 提供用于访问DB数据库的各种接口.
 * 
 * @author jrwz
 * @version 2012-5-23下午11:27:44
 * @see com.lineage
 * @since JDK1.7
 */
public final class L1DatabaseFactory {

    /** 提示信息. */
    private static final Logger LOG = Logger.getLogger(L1DatabaseFactory.class
            .getName());

    /** 实例. */
    private static L1DatabaseFactory _instance;

    /** 数据库连接的来源. */
    private ComboPooledDataSource _source;

    /** 数据库连接的驱动程序. */
    private static String _driver;

    /** 数据库连接的路径. */
    private static String _url;

    /** 登入数据库的账号. */
    private static String _user;

    /** 登入数据库的密码. */
    private static String _password;

    /**
     * 取得数据库的实例（第一次实例为 null 的时候才新建立一个).
     * 
     * 
     * @throws SQLException
     *             该异常提供关于数据库访问错误或其他错误的信息。<br>
     * 
     *             每个 SQLException 都可提供以下多种消息：<br>
     * 
     *             描述错误的字符串。<br>
     *             此字符串用作 Java Exception 消息，可以通过方法 getMesage 使用。<br>
     *             "SQLstate" 字符串，该字符串遵守 XOPEN SQLstate 约定或 SQL 99 约定。<br>
     *             SQLState 字符串的值在适当的规范中描述。<br>
     *             DatabaseMetaData 的方法 getSQLStateType<br>
     *             可用于发现驱动程序返回 XOPEN 类型还是 SQL 99 类型。<br>
     *             特定于每个供应商的整数错误代码。通常，这将是底层数据库返回的实际错误代码。<br>
     *             到下一个 Exception 的链接。可以使用此链接提供其他错误信息。
     * @return L1DatabaseFactory
     */
    public static L1DatabaseFactory getInstance() throws SQLException {
        if (_instance == null) {
            _instance = new L1DatabaseFactory();
        }
        return _instance;
    }

    /**
     * 设置数据库登入的相关资讯.
     * 
     * @param driver
     *            数据库连接的驱动程序
     * @param url
     *            数据库连接的路径
     * @param user
     *            登入数据库的使用者名称
     * @param password
     *            登入数据库的密码
     */
    public static void setDatabaseSettings(final String driver,
            final String url, final String user, final String password) {
        _driver = driver;
        _url = url;
        _user = user;
        _password = password;
    }

    /**
     * 数据库连接的设定与配置.
     * 
     * @throws SQLException
     *             该异常提供关于数据库访问错误或其他错误的信息。<br>
     * 
     *             每个 SQLException 都可提供以下多种消息：<br>
     * 
     *             描述错误的字符串。<br>
     *             此字符串用作 Java Exception 消息，可以通过方法 getMesage 使用。<br>
     *             "SQLstate" 字符串，该字符串遵守 XOPEN SQLstate 约定或 SQL 99 约定。<br>
     *             SQLState 字符串的值在适当的规范中描述。<br>
     *             DatabaseMetaData 的方法 getSQLStateType<br>
     *             可用于发现驱动程序返回 XOPEN 类型还是 SQL 99 类型。<br>
     *             特定于每个供应商的整数错误代码。通常，这将是底层数据库返回的实际错误代码。<br>
     *             到下一个 Exception 的链接。可以使用此链接提供其他错误信息。
     */
    public L1DatabaseFactory() throws SQLException {
        try {
            this._source = new ComboPooledDataSource();
            this._source.setDriverClass(_driver);
            this._source.setJdbcUrl(_url);
            this._source.setUser(_user);
            this._source.setPassword(_password);

            // 测试连接
            this._source.getConnection().close();
        } catch (final SQLException x) {
            LOG.fine("数据库连接失败");
            // 重新抛出异常
            throw x;
        } catch (final Exception e) {
            LOG.fine("数据库连接失败");
            throw new SQLException("无法初始化DB连接: " + e);
        }
    }

    /**
     * 取得数据库连接时的连线.
     * 
     * @return Connection 连接对象
     */
    public Connection getConnection() {
        Connection con = null;

        while (con == null) {
            try {
                con = this._source.getConnection();
            } catch (final SQLException e) {
                LOG.warning("连接数据库失败, 自动重试 " + e);
            }
        }
        // XXX 此处待修改
        return ConfigureSQL.DETECT_DB_RESOURCE_LEAKS ? LeakCheckConnection
                .create(con) : con;
    }

    /**
     * 服务器关闭的时候要关闭与数据库的连接.
     */
    public void shutdown() {
        try {
            this._source.close();
        } catch (final Exception e) {
            LOG.log(Level.INFO, "", e);
        }
        try {
            this._source = null;
        } catch (final Exception e) {
            LOG.log(Level.INFO, "", e);
        }
    }
}
